#include "stdafx.h"
#include "ContentManager.h"

namespace Framework {

ContentManager::ContentManager(Game* game) : game(game) {
}

ContentManager::~ContentManager(void) {
    for (map<LPCSTR, ID3DXEffect*>::const_iterator it = this->effects.begin(); it != this->effects.end(); ++it) {
        d3d::Release(it->second);
    }

    this->effects.clear();

    for (map<LPCSTR, D3DXMesh*>::const_iterator it = this->meshes.begin(); it != this->meshes.end(); ++it) {
        d3d::Delete(it->second);
    }

    this->meshes.clear();

    for (map<LPCSTR, IDirectSoundBuffer*>::const_iterator it = this->primarySoundBuffers.begin(); it != this->primarySoundBuffers.end(); ++it) {
        d3d::Release(it->second);
    }

    this->primarySoundBuffers.clear();

    for (map<LPCSTR, IDirect3DBaseTexture9*>::const_iterator it = this->textures.begin(); it != this->textures.end(); ++it) {
        d3d::Release(it->second);
    }

    this->textures.clear();
}

IDirect3DCubeTexture9* ContentManager::LoadCubeTexture(LPCSTR filename) {
    map<LPCSTR, IDirect3DBaseTexture9*>::iterator it = this->textures.find(filename);
    if (it != this->textures.end()) {
        return (IDirect3DCubeTexture9*)(*it).second;
    }

    IDirect3DCubeTexture9* texture = NULL;
    HRESULT hr = NULL;

    std::string path = this->game->GetWorkingDirectory();
    SetCurrentDirectory("Resources/Textures");
    hr = D3DXCreateCubeTextureFromFile(this->game->GetGraphicsDevice(), filename, &texture);
    SetCurrentDirectory(path.c_str());

    this->textures.insert(pair<LPCSTR, IDirect3DBaseTexture9*>(filename, texture));
    return texture;
}

ID3DXEffect* ContentManager::LoadEffect(LPCSTR filename) {
    map<LPCSTR, ID3DXEffect*>::iterator it = this->effects.find(filename);
    if (it != this->effects.end()) {
        return (*it).second;
    }

    ID3DXEffect* effect = NULL;
    HRESULT hr = NULL;
    ID3DXBuffer* errorBuffer = NULL;

    DWORD dwShaderFlags = D3DXFX_NOT_CLONEABLE;

#if defined( DEBUG ) || defined( _DEBUG )
    // Set the D3DXSHADER_DEBUG flag to embed debug information in the shaders.
    // Setting this flag improves the shader debugging experience, but still allows
    // the shaders to be optimized and to run exactly the way they will run in
    // the release configuration of this program.
    dwShaderFlags |= D3DXSHADER_DEBUG;
#endif

#ifdef DEBUG_VS
    dwShaderFlags |= D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT;
#endif
#ifdef DEBUG_PS
    dwShaderFlags |= D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT;
#endif

    std::string path = this->game->GetWorkingDirectory();
    SetCurrentDirectory("Resources/Effects");
    hr = D3DXCreateEffectFromFile(this->game->GetGraphicsDevice(), filename, NULL, NULL, dwShaderFlags, NULL, &effect, &errorBuffer);
    SetCurrentDirectory(path.c_str());
#if defined( DEBUG ) || defined( _DEBUG )
    if (errorBuffer) {
        MessageBox(NULL, (char *) errorBuffer->GetBufferPointer(), 0, MB_OK);
        errorBuffer->Release();
    }
#endif

    this->effects.insert(pair<LPCSTR, ID3DXEffect*>(filename, effect));
    return effect;
}

D3DXMesh* ContentManager::LoadMesh(LPCSTR filename) {
    map<LPCSTR, D3DXMesh*>::iterator it = this->meshes.find(filename);
    if (it != this->meshes.end()) {
        return (*it).second;
    }

    ID3DXMesh* mesh = NULL;
    ID3DXBuffer* adjBuffer  = NULL;
    ID3DXBuffer* mtrlBuffer = NULL;
    DWORD        numMtrls   = 0;
    HRESULT hr = NULL;

    std::string path = this->game->GetWorkingDirectory();
    SetCurrentDirectory("Resources/Meshes");
    hr = D3DXLoadMeshFromX(
             filename,
             D3DXMESH_MANAGED,
             this->game->GetGraphicsDevice(),
             &adjBuffer,
             &mtrlBuffer,
             0,
             &numMtrls,
             &mesh);
    SetCurrentDirectory(path.c_str());

    D3DXMesh* d3dxmesh = new D3DXMesh(mesh);

    if (mtrlBuffer != 0 && numMtrls != 0) {
        D3DXMATERIAL* mtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer();

        for (UINT i = 0; i < numMtrls; i++) {
            // the MatD3D property doesn't have an ambient value set
            // when its loaded, so set it now:
            mtrls[i].MatD3D.Ambient = mtrls[i].MatD3D.Diffuse;

            // save the ith material
            d3dxmesh->GetMaterials()->push_back( mtrls[i].MatD3D );

            // check if the ith material has an associative texture
            if (mtrls[i].pTextureFilename != 0) {
                // yes, load the texture for the ith subset
                IDirect3DTexture9* tex = this->LoadTexture(mtrls[i].pTextureFilename);
                // save the loaded texture
                d3dxmesh->GetTextures()->push_back(tex);
            } else {
                // no texture for the ith subset
                d3dxmesh->GetTextures()->push_back(NULL);
            }
        }
    }

    // done w/ buffer
    d3d::Release<ID3DXBuffer*>(mtrlBuffer);

    // Optimize the mesh.
    hr = mesh->OptimizeInplace(
             D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT | D3DXMESHOPT_VERTEXCACHE,
             (DWORD*)adjBuffer->GetBufferPointer(),
             0,
             0,
             0);

    // done w/ buffer
    d3d::Release<ID3DXBuffer*>(adjBuffer);

    this->meshes.insert(pair<LPCSTR, D3DXMesh*>(filename, d3dxmesh));
    return d3dxmesh;
}

Sound* ContentManager::LoadSound(LPCSTR filename) {
    IDirectSoundBuffer* buffer = this->LoadWaveFile(filename);
    if (buffer == NULL) {
        return NULL;
    }

    return new Sound(buffer);
}

IDirect3DTexture9* ContentManager::LoadTexture(LPCSTR filename) {
    map<LPCSTR, IDirect3DBaseTexture9*>::iterator it = this->textures.find(filename);
    if (it != this->textures.end()) {
        return (IDirect3DTexture9*)(*it).second;
    }

    IDirect3DTexture9* texture = NULL;
    HRESULT hr = NULL;

    std::string path = this->game->GetWorkingDirectory();
    SetCurrentDirectory("Resources/Textures");
    hr = D3DXCreateTextureFromFile(this->game->GetGraphicsDevice(), filename, &texture);
    SetCurrentDirectory(path.c_str());

    this->textures.insert(pair<LPCSTR, IDirect3DBaseTexture9*>(filename, texture));
    return texture;
}

IDirect3DVolumeTexture9* ContentManager::LoadVolumeTexture(LPCSTR filename) {
    map<LPCSTR, IDirect3DBaseTexture9*>::iterator it = this->textures.find(filename);
    if (it != this->textures.end()) {
        return (IDirect3DVolumeTexture9*)(*it).second;
    }

    IDirect3DVolumeTexture9* texture = NULL;
    HRESULT hr = NULL;

    std::string path = this->game->GetWorkingDirectory();
    SetCurrentDirectory("Resources/Textures");
    hr = D3DXCreateVolumeTextureFromFile(this->game->GetGraphicsDevice(), filename, &texture);
    SetCurrentDirectory(path.c_str());

    this->textures.insert(pair<LPCSTR, IDirect3DBaseTexture9*>(filename, texture));
    return texture;
}

IDirectSoundBuffer* ContentManager::LoadWaveFile(LPCSTR filename) {
    map<LPCSTR, IDirectSoundBuffer*>::iterator it = this->primarySoundBuffers.find(filename);
    if (it != this->primarySoundBuffers.end()) {
        return (IDirectSoundBuffer*)(*it).second;
    }

    std::string path = this->game->GetWorkingDirectory();
    SetCurrentDirectory("Resources/Sounds");

    //open a wav file
    HMMIO wavefile;
    wavefile = mmioOpen((LPSTR)filename, 0, MMIO_READ | MMIO_ALLOCBUF);
    if (wavefile==NULL) {
        Error("LoadDirectSound mmioOpen");
        return NULL;
    }

    //find wave data
    MMCKINFO parent;
    memset(&parent,0,sizeof(MMCKINFO));
    parent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
    mmioDescend(wavefile, &parent, 0, MMIO_FINDRIFF);

    //find fmt data
    MMCKINFO child;
    memset(&child,0,sizeof(MMCKINFO));
    child.fccType = mmioFOURCC('f', 'm', 't', ' ');
    mmioDescend(wavefile, &child, &parent, 0);

    //read the format
    WAVEFORMATEX wavefmt;
    mmioRead(wavefile, (char*)&wavefmt, sizeof(WAVEFORMATEX));
    if (wavefmt.wFormatTag != WAVE_FORMAT_PCM) {
        Error("LoadDirectSound mmioRead");
        return NULL;
    }

    //find the wave data chunk
    mmioAscend(wavefile, &child, 0);
    child.ckid = mmioFOURCC('d', 'a', 't', 'a');
    mmioDescend(wavefile, &child, &parent, MMIO_FINDCHUNK);

    //create a directsound buffer to hold wave data
    DSBUFFERDESC bufdesc;
    memset(&bufdesc, 0, sizeof(DSBUFFERDESC));
    bufdesc.dwSize = sizeof(DSBUFFERDESC);
    bufdesc.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_CTRLVOLUME;
    bufdesc.dwBufferBytes = child.cksize;
    bufdesc.lpwfxFormat = &wavefmt;

    IDirectSoundBuffer* buffer;
    if (DS_OK != (this->game->GetSoundDevice()->GetDirectSound()->CreateSoundBuffer(&bufdesc, (LPDIRECTSOUNDBUFFER*)&buffer, NULL))) {
        Error("LoadDirectSound CreateSoundBuffer");
        return NULL;
    }

    //write wave data to directsound buffer you just created
    void *write1=0,*write2=0;
    unsigned long length1,length2;
    buffer->Lock(0, child.cksize, &write1, &length1, &write2, &length2, 0);
    if (write1>0) {
        mmioRead(wavefile, (char*)write1, length1);
    }

    if (write2>0) {
        mmioRead(wavefile, (char*)write2, length2);
    }

    buffer->Unlock(write1, length1, write2, length2);

    //close the wavefile, don't need it anymore, it's in the directsound buffer now
    mmioClose(wavefile,0);
    SetCurrentDirectory(path.c_str());

    this->primarySoundBuffers.insert(pair<LPCSTR, IDirectSoundBuffer*>(filename, buffer));
    return buffer;
}

}
